package fuzzylizard.teamdocs.domain;


import java.util.List;

import org.joda.time.DateTime;

import fuzzylizard.teamdocs.exceptions.IllegalNameException;

public abstract class File extends PersistedObject {

    private static final String VALID_DIRECTORY_NAME_PATTERN = "[a-z|A-Z|0-9|_]*";

    /**
     * The name of the file
     */
    private String name;

    /**
     * File's parent file
     */
    private File parent;

    /**
     * the date and time the file was first created
     */
    private DateTime created;

    /**
     * the user who created the file
     */
    private User createdBy;

    /**
     * Protected constructor for creating files. This should only be called from within subclasses
     * @param name The name of the file
     */
    protected File(String name) {
        setName(name);
        created = new DateTime();
    }

    /**
     * Protected constructor for creating files. This should only be called from within subclasses
     * @param name The name of the file
     * @param createdBy The user who created the file
     */
    protected File(String name, User createdBy) {
        this(name);
        this.createdBy = createdBy;
    }

    /**
     * Give the file a name
     * @param name File name
     */
    public void setName(String name) {
        validateName(name);
        this.name = name;
    }

    /**
     * Get the file name
     * @return the file's name
     */
    public String name() {
        return name;
    }

    /**
     * The date and time the file was created
     * @return When the file was first created
     */
    public DateTime created() {
        return created;
    }

    /**
     * Set this file's parent file
     * @param parent The file in which the file resides
     */
    public void setParent(File parent) {
        this.parent = parent;
    }

    /**
     * Removes the parent file, this is useful when moving or deleting the file
     */
    public void clearParent() {
        parent = null;
    }

    /**
     * Get the file's parent file
     * @return The parent file
     */
    public File parent() {
        return parent;
    }

    /**
     * The User who created the file
     * @return User
     */
    public User createdBy() {
        return createdBy;
    }

    /**
     * Add a file as a child file. When implementing this method, you should also implement code
     * that sets the child file's parent.
     * 
     * @param file The child file
     * @return True if adding the file worked
     */
    public abstract boolean addFile(File file);

    /**
     * Retrieves a child file
     * @param file Child File to retrieve
     * @return Child File if it exists, otherwise null
     */
    public abstract File getFile(File file);

    /**
     * Removes the child file from the collection. This method should also remove the parent reference
     * from the child
     *
     * @param file File to be removed
     * @return The child file if it exists, otherwise null
     */
    public abstract File removeFile(File file);

    /**
     * Determines whether the file exists as a child file
     * @param file The file to look for
     * @return true if it exists in this file, false otherwise
     */
    public abstract boolean contains(File file);

    /**
     * Retrieve a list of all files contained in this one
     * @return List of all child files
     */
    public abstract List<File> files();
    

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[Document - name: " + name + "]");
        return sb.toString();
    }

    @Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        return fieldsEqual((File) obj);
    }

    private boolean fieldsEqual(final File other) {
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    
    private void validateName(String name) {
        if (!name.matches(VALID_DIRECTORY_NAME_PATTERN))
            throw new IllegalNameException("Invalid File Name");
    }

}